#ifndef THREAD_BLOCKINGQUEUE_H
#define THREAD_BLOCKINGQUEUE_H

#include "utility/Noncopyable.h"

#include <condition_variable>
#include <deque>
#include <mutex>


template<typename T>
class BlockingQueue {
public:
    BlockingQueue(): _deque{}, _cvEmpty{}, _mutex{} {}
    ~BlockingQueue() = default;

    std::size_t size() const {
        const std::lock_guard guard{_mutex};
        return _deque.size();
    }

    void clear() {
        const std::lock_guard guard{_mutex};
        _deque.clear();
    }
    bool is_empty() {
        const std::lock_guard guard{_mutex};
        return _deque.empty(); 
    }

    T pop();
    void push(const T &x);
    void push(T &&x);

private:
    NONCOPYABLE(BlockingQueue);

    std::deque<T> _deque;
    std::condition_variable _cvEmpty;
    mutable std::mutex _mutex;

};

template<typename T>
T BlockingQueue<T>::pop() {
    std::unique_lock lock{_mutex};
    _cvEmpty.wait(lock, [this](){ return !_deque.empty(); });
    T result = std::move(_deque.front());
    _deque.pop_front();
    return result;
}

template<typename T>
void BlockingQueue<T>::push(const T &x) {
    const std::lock_guard guard{_mutex};
    _deque.push_back(x);
    _cvEmpty.notify_one();
}

template<typename T>
void BlockingQueue<T>::push(T &&x) {
    const std::lock_guard guard{_mutex};
    _deque.push_back(std::move(x));
    _cvEmpty.notify_one();
}


#endif